#include "parser.h"


void parser::cmp_run(lex &p)
{
    value_t tmpval1, tmpval2;
    switch (p.get_type()) {
    case LEX_EQ:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) == *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value == tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value == tmpval2.val.i_value);
        }

        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);

        break;
    case LEX_NEQ:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();

        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) != *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value != tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value != tmpval2.val.i_value);
        }

        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);
        break;
    case LEX_LEQ:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();

        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) <= *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value <= tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value <= tmpval2.val.i_value);
        }

        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);
        break;
    case LEX_GEQ:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();

        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) >= *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value >= tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value >= tmpval2.val.i_value);
        }


        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);
        break;
    case LEX_LESS:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();

        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) < *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value < tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value < tmpval2.val.i_value);
        }


        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);
        break;
    case LEX_GREATER:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();

        if (tmpval1.type == LEX_STRING) {
            tmpval1.val.b_value =  *(tmpval1.val.s_value) > *(tmpval2.val.s_value);
        } else if (tmpval1.type ==  LEX_BOOL) {
            tmpval1.val.b_value = (tmpval1.val.b_value > tmpval2.val.b_value);
        } else if (tmpval1.type == LEX_INT) {
            tmpval1.val.b_value = (tmpval1.val.i_value > tmpval2.val.i_value);
        }


        tmpval1.type = LEX_BOOL;
        st_run.push(tmpval1);
        break;
    default:
        break;
    }
}

void parser::assign_run(lex &p)
{
    value_t val1;
    value_t tmpval1, tmpval2;
    stack<value_t> ws;
    switch (p.get_type()) {
    case LEX_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);

        break;
    case LEX_PLUS_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        val1 = m_scan.get_tid()[tmpval1.val.i_value].get_value();
        st_run.pop();
        if (tmpval2.type == LEX_STRING) {
            *(tmpval2.val.s_value) = *(val1.val.s_value) + *(tmpval2.val.s_value);
        } else if (tmpval2.type == LEX_INT) {
            tmpval2.val.i_value = val1.val.i_value + tmpval2.val.i_value;

        } else if (tmpval2.type == LEX_BOOL){
            tmpval2.val.b_value = val1.val.b_value || tmpval2.val.i_value;
        }
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);
        break;

    case LEX_MINUS_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        val1 = m_scan.get_tid()[tmpval1.val.i_value].get_value();
        if (tmpval2.type == LEX_INT) {
            tmpval2.val.i_value = val1.val.i_value - tmpval2.val.i_value;
        }
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);
        break;

    case LEX_TIMES_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        val1 = m_scan.get_tid()[tmpval1.val.i_value].get_value();
        if (tmpval2.type == LEX_INT) {
            tmpval2.val.i_value = val1.val.i_value * tmpval2.val.i_value;
        }
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);
        break;
    case LEX_SLASH_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        val1 = m_scan.get_tid()[tmpval1.val.i_value].get_value();
        if (tmpval2.type == LEX_INT) {
            if (tmpval2.val.i_value == 0) {
                throw "Division by zero";
            }
            tmpval2.val.i_value = val1.val.i_value / tmpval2.val.i_value;
        }
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);
        break;

    case LEX_MOD_ASSIGN:
        tmpval2 = st_run.top();
        st_run.pop();
        tmpval1 = st_run.top();
        st_run.pop();
        val1 = m_scan.get_tid()[tmpval1.val.i_value].get_value();
        if (tmpval2.type == LEX_INT) {
            if (tmpval2.val.i_value == 0) {
                throw "Division by zero";
            }
            tmpval2.val.i_value = val1.val.i_value % tmpval2.val.i_value;
        }
        m_scan.get_tid()[tmpval1.val.i_value].set_value(tmpval2);
        m_scan.get_tid()[tmpval1.val.i_value].set_assigned();
        st_run.push(tmpval2);
        break;
    }
}






void parser::run()
{
    m_prog.finalize();
    value_t tmpval1, tmpval2, tmpval3;
    for (int i = 0; i < m_prog.size(); i++) {
        string str;
        stack<value_t> ws;
        if (is_cmp(m_prog[i].get_type())) {
            cmp_run(m_prog[i]);
        } else if (is_assign(m_prog[i].get_type())) {
            assign_run(m_prog[i]);

        } else
            switch (m_prog[i].get_type())
            {
            //types
            case LEX_STRING:
                tmpval1.val.s_value = &(m_scan.get_ts()[m_prog[i].get_val()]);
                tmpval1.type = LEX_STRING;
                st_run.push(tmpval1);
                break;

            case POLIZ_ADDR:
                tmpval1.val.i_value = m_prog[i].get_val();
                tmpval1.type = POLIZ_ADDR;
                st_run.push(tmpval1);
                break;
            case POLIZ_ARGSBEGIN:
                tmpval1.val.i_value = 0;
                tmpval1.type = POLIZ_ARGSBEGIN;
                st_run.push(tmpval1);
                break;
            case POLIZ_LABEL:
                tmpval1.val.i_value = m_prog[i].get_val();
                tmpval1.type = LEX_INT;
                st_run.push(tmpval1);
                break;
            case LEX_NUM:
            case LEX_INT:
                tmpval1.val.i_value = m_prog[i].get_val();
                tmpval1.type = LEX_INT;
                st_run.push(tmpval1);
                break;
            case LEX_IDENT:
                st_run.push(m_scan.get_tid()[m_prog[i].get_val()].get_value());
                break;
            case LEX_TRUE:
                tmpval1.val.b_value = true;
                tmpval1.type = LEX_BOOL;
                st_run.push(tmpval1);
                break;
            case LEX_FALSE:
                tmpval1.val.b_value = false;
                tmpval1.type = LEX_BOOL;
                st_run.push(tmpval1);
                break;
            case POLIZ_POP:
                st_run.pop();
                break;
            case LEX_UNARY_MINUS:
                st_run.top().val.i_value = -st_run.top().val.i_value;
            case LEX_UNARY_PLUS:
                break;

            case LEX_PLUS:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();

                if (tmpval1.type == LEX_INT) {
                    tmpval3.val.i_value = tmpval1.val.i_value;
                    tmpval3.val.i_value += tmpval2.val.i_value;
                } else if (tmpval1.type == LEX_STRING) {
                    tmpval3.val.s_value = new string();
                    *(tmpval3.val.s_value) = *(tmpval1.val.s_value);
                    *(tmpval3.val.s_value) += *(tmpval2.val.s_value);
                }
                st_run.push(tmpval3);
                break;


            case LEX_MINUS:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                tmpval1.val.i_value -= tmpval2.val.i_value;
                st_run.push(tmpval1);
                break;

            case LEX_TIMES:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                tmpval1.val.i_value *= tmpval2.val.i_value;
                st_run.push(tmpval1);
                break;

            case LEX_SLASH:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                if (!tmpval2.val.i_value) {
                    throw string("Division by zero");
                }
                tmpval1.val.i_value /= tmpval2.val.i_value;
                st_run.push(tmpval1);
                break;

            case LEX_MOD:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                if (!tmpval2.val.i_value) {
                    throw string("Division by zero");
                }
                tmpval1.val.i_value %= tmpval2.val.i_value;
                st_run.push(tmpval1);
                break;


            case LEX_AND:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                tmpval1.val.b_value = tmpval1.val.b_value && tmpval2.val.b_value;
                st_run.push(tmpval1);
                break;

            case LEX_OR:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();
                tmpval1.val.b_value = tmpval1.val.b_value || tmpval2.val.b_value;
                st_run.push(tmpval1);
                break;

            case POLIZ_JMP:
                tmpval1 = st_run.top();
                st_run.pop();
                i = tmpval1.val.i_value - 1;
                break;

            case POLIZ_FJMP:
                tmpval2 = st_run.top();
                st_run.pop();
                tmpval1 = st_run.top();
                st_run.pop();

                if (!tmpval1.val.b_value) {
                    i = tmpval2.val.i_value - 1;
                }
                break;

            case LEX_READ:
                tmpval2 = st_run.top();
                st_run.pop();
                if (tmpval2.type != POLIZ_ADDR) {
                    throw "Invalid Read";
                }
                if (m_scan.get_tid()[tmpval2.val.i_value].get_type() == LEX_INT) {
                    tmpval1.type = LEX_INT;
                    cin >> tmpval1.val.i_value;
                }

                if (m_scan.get_tid()[tmpval2.val.i_value].get_type() == LEX_STRING) {
                    tmpval1.type = LEX_STRING;
                    cin >> str;
                    tmpval1.val.s_value = &str;
                }
                m_scan.get_tid()[tmpval2.val.i_value].set_value(tmpval1);
                m_scan.get_tid()[tmpval2.val.i_value].set_assigned();
                break;

            case LEX_WRITE:
                while (!ws.empty()) ws.pop();
                while (!st_run.empty() && st_run.top().type != POLIZ_ARGSBEGIN) {
                    ws.push(st_run.top());
                    st_run.pop();
                }

                if (st_run.top().type == POLIZ_ARGSBEGIN) {
                    st_run.pop();
                } else {
                    throw "Stack Corruption occured";
                }
                while (!ws.empty()) {
                    if (ws.top().type == LEX_INT) {
                        cout << ws.top().val.i_value << endl;
                    }
                    if (ws.top().type == LEX_BOOL) {
                        if (ws.top().val.b_value) {
                            cout << "true" ;
                        } else {
                            cout << "false";
                        }

                    }

                    if (ws.top().type == LEX_STRING) {
                        cout << *(ws.top().val.s_value) << endl;
                    }
                    ws.pop();
                }
                break;


            default:
                break;

            }

    }
    if (st_run.size()) {
        throw "RUN STACK not empty";
    }
}


parser::parser(const string &fname) : m_scan(fname)
{
    expr_was_const = false;
    prev_lex = lex();
}


void parser::P()
{
    if (curr_lex.get_type() == LEX_PROGRAM) {
        gl();
    } else {
        throw m_scan.get_lineno() + curr_lex.to_string();
    }
    if (curr_lex.get_type() == LEX_BEGIN) {
        gl();
    } else {
        throw m_scan.get_lineno() + "{ expected";
    }
    D1();
    M();
    if (curr_lex.get_type() == LEX_END) {
        gl();
    } else{
        throw m_scan.get_lineno() + "} expected";
    }

    if (curr_lex.get_type() != LEX_FIN) {
        throw m_scan.get_lineno() + curr_lex.to_string();
    }

}

void parser::check_id(bool push) {
    if (m_scan.get_tid()[curr_lex.get_val()].is_declared()) {
        if (push) {
            st_lex.push(m_scan.get_tid()[curr_lex.get_val()].get_type());
        }
    } else {
        throw m_scan.get_lineno() + m_scan.get_tid()[curr_lex.get_val()].get_name() + string(" is not declared");
    }
}

bool parser::is_assign(lex_type_t op)
{
    return (op == LEX_ASSIGN || op == LEX_PLUS_ASSIGN || op == LEX_MINUS_ASSIGN
            || op == LEX_TIMES_ASSIGN) || (op == LEX_SLASH_ASSIGN)
            || (op == LEX_MOD_ASSIGN);
}

bool parser::is_cmp(lex_type_t op) {

    return (op == LEX_EQ) || (op == LEX_LEQ) || (op == LEX_GEQ) || (op == LEX_NEQ)
            || (op == LEX_LESS) || (op == LEX_GREATER);
}


void parser::check_op() {
    lex_type_t oper2, oper1, op, need_type;
    oper2 = st_lex.top();
    st_lex.pop();
    op = st_lex.top();
    st_lex.pop();
    oper1 = st_lex.top();
    st_lex.pop();

    if (op == LEX_AND || op == LEX_OR) {
        need_type = LEX_BOOL;
    }

    if (oper1 == LEX_STRING && (op == LEX_PLUS_ASSIGN || op == LEX_PLUS) && oper2 == LEX_STRING) {
        st_lex.push(LEX_STRING);
        return;
    }

    if (op == LEX_PLUS_ASSIGN || op == LEX_PLUS
            || op == LEX_MINUS || op == LEX_MINUS_ASSIGN
            || op == LEX_TIMES || op == LEX_TIMES_ASSIGN
            || op == LEX_SLASH || op == LEX_SLASH_ASSIGN
            || op == LEX_SLASH || op == LEX_SLASH_ASSIGN) {
        need_type = LEX_INT;
    }

    if (is_cmp(op)) {
        if (oper1 == oper2) {
            st_lex.push(LEX_BOOL);
            return;
        } else {
            m_scan.get_lineno() + " Type mismatch";
        }
    }

    if (oper1 == oper2 && oper1 == need_type) {
        st_lex.push(need_type);
    } else {
        throw  m_scan.get_lineno() + " Type mismatch";
    }

}

void parser::eq_type()
{
    lex_type_t op1, op2;
    op1 = st_lex.top();
    st_lex.pop();
    op2 = st_lex.top();
    st_lex.pop();
    if (!(op1 == op2)) {
        throw m_scan.get_lineno() + string("Invalid types in Assignation");
    }
}


void parser::check_not() {
    if (st_lex.top() != LEX_BOOL && st_lex.top() != LEX_INT) {
        throw m_scan.get_lineno() + string("Invalid use of Not");
    }
}

void parser::check_bool() {
    if (st_lex.top() == LEX_BOOL) {
        st_lex.pop();
    } else {
        throw m_scan.get_lineno() + string("Type of expression must be bool");
    }
}

bool parser::is_type()
{
    return (curr_lex.get_type() == LEX_INT_DECL) || (curr_lex.get_type() == LEX_STRING_DECL) ||
            (curr_lex.get_type() == LEX_BOOL_DECL);
}

void parser::D1()
{
    do {
        lex_type_t  type_lex = curr_lex.get_type(); //save type
        if (!is_type()) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        switch (type_lex) {
        case LEX_INT_DECL:
            type_lex = LEX_INT;
            break;
        case LEX_BOOL_DECL:
            type_lex = LEX_BOOL;
            break;
        case LEX_STRING_DECL:
            type_lex = LEX_STRING;
            break;
        default:
            break;
        }

        gl();
        if (curr_lex.get_type() != LEX_IDENT) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }

        if (m_scan.get_tid()[curr_lex.get_val()].is_declared()) {
            throw string("Redeclaration of variable") + m_scan.get_tid()[curr_lex.get_val()].get_name();
        } else {
            m_scan.get_tid()[curr_lex.get_val()].set_declared();
            m_scan.get_tid()[curr_lex.get_val()].set_type(type_lex);
        }


        gl();
        while (curr_lex.get_type() == LEX_COMMA) {
            gl();
            if (curr_lex.get_type() != LEX_IDENT) {
                throw m_scan.get_lineno() + curr_lex.to_string();
            } else { //declare here
                if (m_scan.get_tid()[curr_lex.get_val()].is_declared()) {
                    throw string("Redeclaration of variable") + m_scan.get_tid()[curr_lex.get_val()].get_name();
                } else {
                    m_scan.get_tid()[curr_lex.get_val()].set_declared();
                    m_scan.get_tid()[curr_lex.get_val()].set_type(type_lex);
                }
                gl();
            }
        }
        if (curr_lex.get_type() != LEX_SEMICOLON) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();
    } while (is_type());
}

void parser::M() // main
{
    S();
    while (curr_lex.get_type() != LEX_END && (curr_lex.get_type() == LEX_SEMICOLON
                                              || prev_lex.get_type() == LEX_END))
    {
        if (curr_lex.get_type() == LEX_SEMICOLON) {
            gl();
        }
        if (curr_lex.get_type() == LEX_END) {
            break;
        }
        S();
    }
}

void parser::B() // composite operator { have already read
{
    S();
    while ((curr_lex.get_type() == LEX_SEMICOLON && curr_lex.get_type() != LEX_END)
           || prev_lex.get_type() == LEX_END) {
        if (prev_lex.get_type() != LEX_END) {
            gl();
        }
        if (curr_lex.get_type() == LEX_END) {
            break;
        }
        S();
    }

    if (curr_lex.get_type() == LEX_END) {
        gl();
    } else {
        throw m_scan.get_lineno() + "} expected";
    }
}


void parser::E()
{
    expr_was_const = false;
    E3();
    /* */
    while (curr_lex.get_type() == LEX_OR) {
        st_lex.push(curr_lex.get_type());
        //semantic actions
        gl();
        E3();
        check_op();
        m_prog.enq(LEX_OR);
        //check types
    }
}

void parser::E3()
{
    E2();
    while (curr_lex.get_type() == LEX_AND) {
        st_lex.push(curr_lex.get_type());
        //semantic actions
        gl();
        E2();
        check_op();
        m_prog.enq(LEX_AND);
        //check types
    }
}


void parser::E2() {
    E1();
    while (curr_lex.get_type() == LEX_EQ ||  curr_lex.get_type() == LEX_LESS
           || curr_lex.get_type() == LEX_GREATER || curr_lex.get_type() == LEX_LEQ
           || curr_lex.get_type() == LEX_GEQ || curr_lex.get_type() == LEX_NEQ) {
        st_lex.push(curr_lex.get_type());
        lex p = curr_lex;
        gl();
        E1();

        check_op();
        m_prog.enq(p);
    }
}


void parser::E1()
{
    T();
    while (curr_lex.get_type() == LEX_PLUS || curr_lex.get_type() == LEX_MINUS) {
        //semantic actions
        lex p = curr_lex;
        st_lex.push(curr_lex.get_type());
        gl();
        T();
        check_op();
        m_prog.enq(p);
        //check types
    }
}

void parser::T()
{
    F();
    while (curr_lex.get_type() == LEX_TIMES || curr_lex.get_type() == LEX_SLASH
           || curr_lex.get_type() == LEX_MOD) {
        lex p = curr_lex;
        st_lex.push(curr_lex.get_type());
        //semantic actions
        gl();
        F();
        check_op();
        m_prog.enq(p);
        //check types
    }
}


void parser::F()
{
    if (curr_lex.get_type() == LEX_IDENT) {
        check_id();
        m_prog.enq(curr_lex);
        //semantic actions check_id(), poliz_actions
        gl();
    }   else if (curr_lex.get_type() == LEX_STRING) {
        st_lex.push(LEX_STRING);
        expr_was_const = true;

        m_prog.enq(curr_lex);
        gl();

        //poliz actions
    } else  if (curr_lex.get_type() == LEX_NUM) {
        st_lex.push(LEX_INT);
        expr_was_const = true;
        m_prog.enq(curr_lex);
        // semantic actions st_lex.push(LEX_INT) // LEX_REAL, LEX_STRING
        //poliz actions
        gl();
    } else if (curr_lex.get_type() == LEX_TRUE || curr_lex.get_type() == LEX_FALSE) {
        expr_was_const = true;
        st_lex.push(LEX_BOOL);
        m_prog.enq(curr_lex);
        gl();
        //poliz actions
    } else if(curr_lex.get_type() == LEX_NOT) {
        lex p = curr_lex;
        gl();
        F();
        m_prog.enq(p);
        check_not();
    } else if (curr_lex.get_type() == LEX_LPAREN) {
        gl();
        st_expr_was_const.push(expr_was_const);
        E();
        if (curr_lex.get_type() != LEX_RPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string() + "parentness brace expected";
        } else {
            expr_was_const = expr_was_const || st_expr_was_const.top();
            st_expr_was_const.pop();
            gl();
        }
    } else if(curr_lex.get_type() == LEX_UNARY_MINUS) {
        gl();
        expr_was_const = true;
        E();
        if (st_lex.top() != LEX_INT) {
            throw m_scan.get_lineno() + curr_lex.to_string() + "Invalid usage of unary minus";
        }
        m_prog.enq(curr_lex);
    } else if (curr_lex.get_type() == LEX_UNARY_PLUS) {
        gl();
        expr_was_const = true;
        E();
        if (st_lex.top() != LEX_INT) {
            throw m_scan.get_lineno() + curr_lex.to_string() + "Invalid usage of unary minus";
        }
        m_prog.enq(curr_lex);
    }
    else {
        throw m_scan.get_lineno() + curr_lex.to_string();
    }
}


void parser::empty_E()
{
    if (curr_lex.get_type() != LEX_SEMICOLON) {
        E();
    }
}

void parser::S(bool disable_complex)
{

    if (curr_lex.get_type() == LEX_IF) {
        gl();

        if (curr_lex.get_type() != LEX_LPAREN) { // expression must be with brackets
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        E();
        check_bool();
        lex p(POLIZ_FJMP);
        m_prog.enq(p);
        if (curr_lex.get_type() != LEX_SEMICOLON) {
            S();
            if (curr_lex.get_type() == LEX_ELSE) {
                m_prog.set_addr(m_prog.size() + 2);// jump to body body wiilb be after next jump instruction
                p.set_type(POLIZ_JMP);
                m_prog.enq(p);
                gl();
                S();
                m_prog.set_addr();
            } else {
                m_prog.set_addr();
            }
        } else {
            m_prog.set_addr();
        }
    } else if (curr_lex.get_type() == LEX_WHILE) { //WHILE
        int addr = m_prog.size();
        st_loops_entryp.push(addr);
        gl();
        if (curr_lex.get_type() != LEX_LPAREN) { // expression must be with brackets parentness bracket should handled in  E();
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        E();
        lex p(POLIZ_FJMP);
        m_prog.enq(p);
        check_bool();
        if (curr_lex.get_type() != LEX_SEMICOLON) {
            S();
            p.set_type(POLIZ_JMP);
            m_prog.enq(p);
            m_prog.set_addr(addr);
        }
        st_loops_entryp.pop();
        m_prog.set_addr();
    } else if (curr_lex.get_type() == LEX_FOR) { // FOR
        gl();

        if (curr_lex.get_type() != LEX_LPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();
        if (curr_lex.get_type() != LEX_SEMICOLON) { //empty pre-action
            S(true);
        }

        if (curr_lex.get_type() != LEX_SEMICOLON) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();

        int action_addr, expr_addr = m_prog.size();
        lex p;
        st_loops_entryp.push(expr_addr);

        if (curr_lex.get_type() != LEX_SEMICOLON) {
            E();
            p.set_type(POLIZ_FJMP); // jump out of  the loop
            m_prog.enq(p);
            p.set_type(POLIZ_JMP); // jump to body
            m_prog.enq(p);
            check_bool();
        } else {
            expr_addr = -1;
        }

        if (curr_lex.get_type() != LEX_SEMICOLON) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        } else {
            action_addr = m_prog.size();
        }
        gl();

        if (curr_lex.get_type() != LEX_RPAREN) { //empty postaction
            S(true);
        }

        p.set_type(POLIZ_JMP);
        m_prog.enq(p);
        if (expr_addr != -1) {
            m_prog.set_addr(expr_addr);
        }// after postaction jump to expression



        if (curr_lex.get_type() != LEX_RPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();
        if (curr_lex.get_type() != LEX_SEMICOLON) {
            m_prog.set_addr();
            S();
            p.set_type(POLIZ_JMP);
            m_prog.enq(p);
            m_prog.set_addr(action_addr);
        } else if (expr_addr != -1) { //if empty body and expression not empty jump to expression
            m_prog.set_addr(action_addr);
        } else {
            m_prog.set_addr(action_addr); // else to action
        }

        st_loops_entryp.pop();

        if (expr_addr != -1) {
            m_prog.set_addr(); // jump out of the body
        }
    } else if (curr_lex.get_type() == LEX_DO) { // DO _WHILE
        gl();
        int entry_addr = m_prog.size();
        if (curr_lex.get_type() != LEX_SEMICOLON) {
            S();
        }
        if (prev_lex.get_type() != LEX_END) {
            if (curr_lex.get_type() != LEX_SEMICOLON) {
                throw m_scan.get_lineno() + "; expected";
            } else {
                gl();
            }
        }
        if (curr_lex.get_type() != LEX_WHILE) {
            throw m_scan.get_lineno() + "while expected";
        }
        gl();
        if (curr_lex.get_type() != LEX_LPAREN) { // expression must be with brackets parentness bracket should handled in  E();
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        E();
        check_bool();

        lex p(POLIZ_FJMP);
        m_prog.enq(p);
        p.set_type(POLIZ_JMP);
        m_prog.enq(p);
        m_prog.set_addr(entry_addr);
        m_prog.set_addr();

    } else if (curr_lex.get_type() == LEX_READ)  {
        gl();
        if (curr_lex.get_type() != LEX_LPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();

        if (curr_lex.get_type() != LEX_IDENT) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        } else {
            check_id(false);
            curr_lex.set_type(POLIZ_ADDR);
            m_prog.enq(curr_lex);
            curr_lex.set_type(LEX_READ);
            m_prog.enq(curr_lex);
        }


        gl();
        if (curr_lex.get_type() != LEX_RPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();
    } else if (curr_lex.get_type() == LEX_WRITE) {
        gl();
        lex p(POLIZ_ARGSBEGIN);
        m_prog.enq(p);
        if (curr_lex.get_type() != LEX_LPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();
        E();

        while (curr_lex.get_type() == LEX_COMMA) {
            gl();
            E();
        }

        p.set_type(LEX_WRITE);
        m_prog.enq(p);

        if (curr_lex.get_type() != LEX_RPAREN) {
            throw m_scan.get_lineno() + curr_lex.to_string();
        }
        gl();

    } else if(curr_lex.get_type() == LEX_IDENT) { // assignation
        lex lex_identif = curr_lex;
        check_id();
        st_lex.push(m_scan.get_tid()[curr_lex.get_val()].get_type());
        lex_type_t saved_type = st_lex.top();
        gl();
        if (!is_assign(curr_lex.get_type())) { // expression-operator
            ungl(curr_lex);
            curr_lex = lex_identif;
            E();
            lex p (POLIZ_POP);
            m_prog.enq(p);
        } else {
            lex p;
            //m_prog.enq(p);
            int first_addr = m_prog.size();
            m_prog.enq(lex_identif);
            stack<lex> st_assign;
            st_assign.push(curr_lex);

            gl();

            int last_addr = m_prog.size();
            E();
            eq_type();
            if (!is_assign(curr_lex.get_type())) {
                expr_was_const = true;
            }
            while (is_assign(curr_lex.get_type()) && !expr_was_const) {

                st_lex.push(saved_type);
                st_assign.push(curr_lex);

                gl();
                last_addr = m_prog.size();
                E();
                eq_type();
            }

            if (expr_was_const && is_assign(curr_lex.get_type())) {
                throw m_scan.get_lineno() + "Can`t assign anything to expression with consts";
            }


            convert_idents(m_prog, first_addr, last_addr);
            while (!st_assign.empty()) {
                m_prog.enq(st_assign.top());
                st_assign.pop();
            }
            p.set_type(POLIZ_POP);
            m_prog.enq(p);

            expr_was_const = false;
        }

    } else if(curr_lex.get_type() == LEX_NUM || curr_lex.get_type() == LEX_STRING
              || curr_lex.get_type() == LEX_BOOL) {
        E();
        lex p(POLIZ_POP);
        m_prog.enq(p);
    } else if (curr_lex.get_type() == LEX_BEGIN) {
        if (disable_complex) {
            throw m_scan.get_lineno() + "Complex operator disabled in this case";
        }
        gl();
        B();
    } else if (curr_lex.get_type() == LEX_CONTINUE) {
        if (st_loops_entryp.empty()) {
            throw m_scan.get_lineno() + "Continue outside of the loop";
        }

        lex p(POLIZ_JMP);
        m_prog.enq(p);
        m_prog.set_addr(st_loops_entryp.top());
    } else {
        throw m_scan.get_lineno() + "Unknown Operator";
    }
}
